package org.orbis.communicate.indexer.term;

import org.msgpack.MessagePackObject;
import org.msgpack.object.ArrayType;
import org.msgpack.object.FloatType;
import org.msgpack.object.IntegerType;
import org.msgpack.object.RawType;
import org.orbis.index.IndexingPackage;

public class TermPackage{
	private String term;
	private long ID;
	private int termFreq;
	private int valueType;
	private int cluster;
	private boolean[] binaryFlags;
	private int[] intFlags;
	private long[] longFlags;
	private float[] floatFlags;

	private TermPackage(){}

	public static TermPackage withFlag(String term, long ID, int termFreq, int valueType,
		IndexingPackage flags){
		TermPackage instance = new TermPackage();
		instance.term = term;
		instance.ID = ID;
		instance.termFreq = termFreq;
		instance.valueType = valueType;
		instance.binaryFlags = flags.binaryFlags();
		instance.intFlags = flags.intFlags();
		instance.longFlags = flags.longFlags();
		instance.floatFlags = flags.floatFlags();

		return instance;
	}

	public static boolean[] byteToFlag(byte[] b, int boolLength){
		boolean[] newset = new boolean[boolLength];

		int counter = 0;
		int filter;
		for(int i = 0; i < b.length; i++){
			filter = 0x80;
			for(int j = 0; j < 8 && counter < newset.length; j++, counter++){
				if((b[i] & filter) != 0) newset[8 * i + j] = true;
				else newset[8 * i + j] = false;
				filter >>= 1;
			}
		}

		return newset;
	}

	public static byte[] flagToByte(boolean[] flags){
		int flagSize = flags.length;
		if(flagSize == 0) return new byte[0];
		byte[] flagArray = new byte[(int)Math.ceil(0.125 * flagSize)];

		int counter = 0;
		for(int i = 0; i < flagArray.length; i++){
			for(int j = 0; j < 8 && counter < flagSize; j++, counter++)
				flagArray[i] += ((flags[8 * i + j]? 1: 0) << (7 - j));
		}

		return flagArray;
	}

	public static final int HEADER = 'p' << 24 | 't' << 16 | 'r' << 8 | 'm';

	public static TermPackage unpack(ArrayType array){
		MessagePackObject[] object = array.asArray();

		int header = object[0].asInt();
		if(header != HEADER) return null;

		TermPackage pkg = new TermPackage();
		pkg.ID = object[1].asLong();
		pkg.termFreq = object[2].asInt();
		pkg.valueType = object[3].asInt();
		pkg.cluster = object[4].asInt();
		pkg.term = object[5].asString();
		int binLength = object[6].asInt();
		pkg.intFlags = new int[object[7].asInt()];
		pkg.longFlags = new long[object[8].asInt()];
		pkg.floatFlags = new float[object[9].asInt()];
		pkg.binaryFlags = byteToFlag(object[10].asByteArray(), binLength);

		int subtotal = 11;
		for(int i = 0; i < pkg.intFlags.length; i++)
			pkg.intFlags[i] = object[subtotal + i].asInt();
		subtotal += pkg.intFlags.length;
		for(int i = 0; i < pkg.longFlags.length; i++)
			pkg.longFlags[i] = object[subtotal + i].asLong();
		subtotal += pkg.longFlags.length;
		for(int i = 0; i < pkg.floatFlags.length; i++)
			pkg.floatFlags[i] = object[subtotal + i].asFloat();

		return pkg;
	}

	public ArrayType pack(){
		MessagePackObject[] objs =
			new MessagePackObject[11 + intFlags.length + longFlags.length + floatFlags.length];

		objs[0] = IntegerType.create(HEADER);
		objs[1] = IntegerType.create(ID);
		objs[2] = IntegerType.create(termFreq);
		objs[3] = IntegerType.create(valueType);
		objs[4] = IntegerType.create(cluster);
		objs[5] = RawType.create(term);
		objs[6] = IntegerType.create(binaryFlags.length);
		objs[7] = IntegerType.create(intFlags.length);
		objs[8] = IntegerType.create(longFlags.length);
		objs[9] = IntegerType.create(floatFlags.length);
		objs[10] = RawType.create(flagToByte(binaryFlags));

		int subtotal = 11;
		for(int i = 0; i < intFlags.length; i++)
			objs[subtotal + i] = IntegerType.create(intFlags[i]);
		subtotal += intFlags.length;
		for(int i = 0; i < longFlags.length; i++)
			objs[subtotal + i] = IntegerType.create(longFlags[i]);
		subtotal += longFlags.length;
		for(int i = 0; i < floatFlags.length; i++)
			objs[subtotal + i] = FloatType.create(floatFlags[i]);

		return ArrayType.create(objs);
	}

	public void setCluster(int cluster){
		this.cluster = cluster;
	}

	public String term(){
		return term;
	}

	public long ID(){
		return ID;
	}

	public int termFreq(){
		return termFreq;
	}

	public int valueType(){
		return valueType;
	}

	public int cluster(){
		return cluster;
	}

	public boolean binaryFlag(int index){
		return binaryFlags[index];
	}

	public boolean[] binaryFlags(){
		return binaryFlags;
	}

	public int intFlag(int index){
		return intFlags[index];
	}

	public int[] intFlags(){
		return intFlags;
	}

	public long longFlag(int index){
		return longFlags[index];
	}

	public long[] longFlags(){
		return longFlags;
	}

	public float floatFlag(int index){
		return floatFlags[index];
	}

	public float[] floatFlags(){
		return floatFlags;
	}

	@Override
	public String toString(){
		StringBuffer result = new StringBuffer("TermPackage: \n");
		result.append("Term: ").append(term).append("\n");
		result.append("ID: ").append(ID).append("\n");
		result.append("Term frequency: ").append(termFreq).append("\n");
		result.append("Value type: ").append(valueType).append("\n");
		result.append("Cluster: ").append(cluster).append("\n");
		for(int i = 0; i < binaryFlags.length; i++){
			result.append("Binary flag ").append(i).append(": ").append(binaryFlags[i]).append("\n");
		}
		for(int i = 0; i < intFlags.length; i++){
			result.append("Integer flag ").append(i).append(": ").append(intFlags[i]).append("\n");
		}
		for(int i = 0; i < longFlags.length; i++){
			result.append("Long flag ").append(i).append(": ").append(longFlags[i]).append("\n");
		}
		for(int i = 0; i < floatFlags.length; i++){
			result.append("Float flag ").append(i).append(": ").append(floatFlags[i]).append("\n");
		}
		return result.toString();
	}
}
